F3. The Main Loop All FaceIt-based programs contain a main event loop that consists of a simple loop around a call to DoLoop: repeat FaceIt(nil,DoLoop,0,0,0,0); [respond here to messages] until false; DoLoop handles "low level" events returned by the toolbox call "WaitNextEvent". FaceIt preprocesses these low-level events and only returns control to the program (returns from DoLoop) when an event occurs that must be handled by the program. Control is returned with a message in the form of a menu or pseudo-menu event (described below). This approach dramatically reduces the complexity of main event loops in FaceIt-based programs without robbing the program of its control over the main loop. This differs from competing "program generators" that generate large amounts of repetitious, complex code, and from high-level programming environments that completely hide the main loop from programmers. Message Types The "messages" returned by FaceIt can be classified on the basis of the value of the fRec variable uMenuID, where uMenuID can be either, • zero (for unprocessed events) • a menu ID (for menu selections) • an FWND ID (for hits in ViewIt windows) • a module's baseID (for module-specific messages) • a custom message number (determined by program) Menu IDs will typically be in the range of 101 to 190 for program menus, FWND IDs in the range of 1000 to 1099 for program windows, and baseIDs in the range of 1100 to 7499 for FaceWare modules. Custom message numbers refer to messages that your program posts to itself. (See the description of PstEvt in "Other Utilities" for discussion of such messages, plus the "faking" of user actions by posting your own events.) Thus a typical FaceIt-based main event loop contains a case (or if...else) block based on the value of uMenuID: repeat FaceIt(nil,DoLoop,0,0,0,0); case uMenuID of 104: [respond to menu selection (menu ID 104)] 1001: [respond to window hit (FWND 1001)] 1100: [respond to module message (FCMD 1100)] otherwise end; until false; The message is then further differentiated by the value of uMenuItem and other fRec variables, where uMenuItem is typically a menu item number, window item ID number, or module-specific message number: repeat FaceIt(nil,DoLoop,0,0,0,0); case uMenuID of 104: if (uMenuItem = 2) then [second item in menu selected] 1001: if (uMenuItem = 4) then [window item hit with ID = 4] 1100: if (uMenuItem = 2) then [active window changed - a FaceIt message] otherwise end; until false; Thus we often refer to the messages returned by FaceIt as "menu" or "pseudo-menu" events since they are always differentiated on the basis of uMenuID and uMenuItem. Other fRec variables such as uString, uResult, wiHit, wvHit, wcHit, wClick, wEvent, and fEvent are often used to return additional info with messages. Which fRec variables are used with each message will be documented with the message. Menu events, for example, are described in the "Menu Handling" topic, window events in "Windows" topic in ViewIt Guide, and FaceIt-specific messages in the description of the DoInit command in "Program Commands" topic. What To Do: If incorporating support for FaceIt into an existing program, add the few lines of initialization code required and a simple DoLoop-based loop like that shown above and in the demo programs. Then, as you add program menu items and windows, add new cases to the loop to handle any messages returned by these. Module Messages FaceWare modules often support special, module-specific messages. As mentioned above, these messages are denoted by the fact that uMenuID returns with the baseID of the module (1100 for FaceIt, 1200 for ViewIt, etc.). FaceIt itself supports several optional messages that can be turned on or off according to the value of parameter a when calling DoInit (as described under DoInit in the "Program Commands" topic). The most commonly used FaceIt message, for example, is the one that returns control to the program when the active window identity changes (i.e., when a new window becomes the active window). The message is enabled by adding 2 to a when calling DoInit, and results in returning uMenuID = 1100 and uMenuItem = 2 after an active window change: ... FaceIt(nil,DoInit,2,0,0,0); ... repeat FaceIt(nil,DoLoop,0,0,0,0); ... else if (uMenuID = 1100) then if (uMenuItem = 2) then [active window changed] ... until false; Which window has become active can then be determined by examing the fActive... variables in fRec. This is useful in cases where the state of program menu or window items must be adjusted to correspond to the active window. Apple Events With the introduction of System 7, Apple added a new type of event, the "Apple event", which can be used to pass information between and within programs. To receive Apple events, the program must be running under System ≥7 and have a SIZE resource with its "High level event aware" option set. FaceIt provides support for the four "required" Apple events (see "Finder Resources" and DoLoop in "Program Commands" topic for further info). All other Apple events are passed to "AEProcessAppleEvent". If your program needs to pre-process such Apple events, then add 512 to parameter a when calling DoInit. This causes FaceIt to return these Apple events from DoLoop with uMenuID = 0 and with the event in fEvent (fEvent.what = 23). You can then examine the event before calling "AEProcessAppleEvent". Foreign Windows When using FaceIt, any non-ViewIt windows that cause update events to occur (have non-empty update regions) are automatically hidden by FaceIt. This is done to prevent such windows from "flooding" the event loop with update events that are never processed properly. In some cases you may prefer to have the update region of such windows cleared instead of hiding the window. The DoInit command supports a bit flag that forces FaceIt to do this instead of hiding windows (see the "Program Commands" topic for further info). If you need to mix your own windows with ViewIt windows, then either use the FaceSt module in place of FaceIt (see the "Hybrid Programs" topic for further info), or use ViewIt windows with controls that are driven by main program code via override procedures ("Override" topic in ViewIt guide). Greater Control (Advanced) As noted above and in other topics, FaceIt and ViewIt take responsibility for handling most low-level events and only return control when an event occurs that must be handled by the program. In some cases, you may need to have greater control over the processing of low-level events. FaceIt and ViewIt support two ways of achieving this added control. • Overrides Nearly all events of interest to a programmer get passed to the control drivers that support controls in ViewIt windows. These events are passed in the form of control messages that can be intercepted via a program override procedure. The "Override" topic in the ViewIt guide describes these procedures, and the "vDemoXY" program contains an example of a procedure that intercepts (and modifies) a key press message. • WaitNextEvent If an override procedure won't satisfy your needs (unlikely), then an alternative is to write a function that FaceIt will call in place of its internal call to WaitNextEvent. This function then sees all raw events and can choose to pass these events back to FaceIt and/or deal with them in a program-specific manner. The program function must be a Pascal-type function that has the same form as the toolbox call "WaitNextEvent", but with one additional parameter as its first parameter. This additional parameter will be the address of fRec, but you will probably ignore this since fRec is likely to be available to the routine as a global variable. In Pascal, the function would be declared as: FUNCTION MyWaitNextEvent (fPtr: FacePtr; eventMask: integer; VAR theEvent: EventRecord; sleep: longint; mouseRgn: RgnHandle) : boolean; This function should contain its own call to WaitNextEvent, plus any other code that is required to do your program-specific handling of the raw events. When calling WaitNextEvent, pass it the same parameters received by your function ("eventMask", "theEvent", "sleep", "mouseRgn"). If the event is to be passed on to FaceIt, then return the result of the WaitNextEvent call as the result of your function. To install the function, simply place its address in the fRec variable fWaitNextEvent (save and later restore the existing value if this action is temporary). FaceIt will then call your function instead of calling WaitNextEvent. Be careful that your function is located in a code resource that will not be moved! Finally, note that your WaitNextEvent replacement function will not be called when a ViewIt modal window is open. This trick only works with FaceIt modeless event handling.